home *** CD-ROM | disk | FTP | other *** search
/ AmigActive 10 / AACD 10.iso / AACD / Resources / Sound / AHI / Developer / examples / Low-level / HardDiskRecord / HardDiskRecord.c < prev    next >
C/C++ Source or Header  |  1997-01-23  |  20KB  |  742 lines

  1. /* This is a quickly made example of how you can use
  2.    AHI to record AIFF/AIFC samples directly to hard disk.
  3.    Like always, it's pooly documentated and quite messy... Sorry.
  4.    This source and its executable is Public Domain.
  5.    Feel free to base any own work on it.
  6.    Done by Martin Blom in 1996.
  7.  
  8.    Notes: Keep your hands away from the mouse and keyboard while the program
  9.    is recording. If the countdown gadget cannot be updated, the program will
  10.    stall and you'll miss samples.
  11.  
  12.    Yeah, yeah, I know the program sucks. It's just an example.
  13.  
  14. */
  15.  
  16. #define NO_PROTOS
  17. #define NO_SAS_PRAGMAS
  18. #include <iffp/8svx.h>
  19. #undef NO_PROTOS
  20. #undef NO_SAS_PRAGMAS
  21.  
  22. #include <exec/lists.h>
  23. #include <exec/memory.h>
  24. #include <dos/dos.h>
  25. #include <devices/ahi.h>
  26. #include <intuition/intuition.h>
  27. #include <intuition/gadgetclass.h>
  28. #include <libraries/asl.h>
  29. #include <libraries/gadtools.h>
  30. #include <proto/ahi.h>
  31. #include <proto/asl.h>
  32. #include <proto/exec.h>
  33. #include <proto/dos.h>
  34. #include <proto/intuition.h>
  35. #include <proto/gadtools.h>
  36.  
  37. #include <stdio.h>
  38. #include <math.h>
  39. #include "GUI.h"
  40. #include "GUI.extras.h"
  41.  
  42. #include "HardDiskRecord.h"
  43.  
  44. #define VERSION_STRING {"\0$VER: HardDiskRecord 1.1 "__AMIGADATE__"\r\n"}
  45. static char VerString[] = VERSION_STRING;
  46.  
  47. //extern void KPrintF(char *fmt,...);
  48.  
  49. struct Library    *AHIBase;
  50. struct MsgPort    *AHImp=NULL;
  51. struct AHIRequest *AHIio=NULL;
  52. BYTE               AHIDevice=-1;
  53. struct AHIAudioModeRequester *amreq=NULL;
  54. struct FileRequester *filereq=NULL;
  55. struct AHIAudioCtrl *actrl=NULL;
  56.  
  57. struct EasyStruct req = {
  58.   sizeof (struct EasyStruct),
  59.   0,
  60.   NULL,
  61.   NULL,
  62.   NULL,
  63.   };
  64.  
  65. char directory[1024],filename[108],fullname[1024];
  66. BPTR file=NULL;
  67.  
  68. #define STRINGNODE_ID 100
  69. #define STRINGNODE_LENGTH 32
  70. struct StringNode
  71. {
  72.   struct  Node sn_Node;
  73.   char    sn_String[STRINGNODE_LENGTH];
  74. };
  75. struct List InputList,OutputList;
  76. char   amtext1[32],amtext2[32];
  77.  
  78. ULONG  audioid=AHI_INVALID_ID,recfreq=0;
  79. ULONG  source=0,dest=0,vol=0,gain=100,duration=0;
  80. ULONG  type=1; //type: 0 = AIFF, 1 = AIFC 
  81. ULONG  stereo=0; //stereo: 0=no, 1 = yes
  82. ULONG  samplesize=0;
  83.  
  84. struct TagItem filtertags[]=
  85. {
  86.   AHIDB_Record,TRUE,
  87.   TAG_DONE
  88. };
  89.  
  90. struct {
  91.   ULONG                 FORMid;
  92.   ULONG                 FORMsize;
  93.   ULONG                 AIFCid;
  94.  
  95.   ULONG                 FVERid;
  96.   ULONG                 FVERsize;
  97.   FormatVersionHeader   FVERchunk;
  98.  
  99.   ULONG                 COMMid;
  100.   ULONG                 COMMsize;
  101.   ExtCommonChunk        COMMchunk;
  102.  
  103.   ULONG                 SSNDid;
  104.   ULONG                 SSNDsize;
  105.   SampledSoundHeader    SSNDchunk;
  106. } AIFCheader = 
  107.   { // All NULLs will be filled later.
  108.     ID_FORM,NULL,ID_AIFC,
  109.     ID_FVER,sizeof(FormatVersionHeader),{AIFCVersion1},
  110.     ID_COMM,sizeof(ExtCommonChunk),{NULL,NULL,16,{NULL},NO_COMPRESSION,
  111.         sizeof("not compressed")-1,'n','o','t',' ','c','o','m','p','r','e','s','s','e','d'},
  112.     ID_SSND,NULL,{0,0}
  113.   };
  114. struct {
  115.   ULONG                 FORMid;
  116.   ULONG                 FORMsize;
  117.   ULONG                 AIFFid;
  118.  
  119.   ULONG                 COMMid;
  120.   ULONG                 COMMsize;
  121.   CommonChunk           COMMchunk;
  122.  
  123.   ULONG                 SSNDid;
  124.   ULONG                 SSNDsize;
  125.   SampledSoundHeader    SSNDchunk;
  126. } AIFFheader = 
  127.   { // All NULLs will be filled later.
  128.     ID_FORM,NULL,ID_AIFF,
  129.     ID_COMM,sizeof(CommonChunk),{NULL,NULL,16,{NULL}},
  130.     ID_SSND,NULL,{0,0}
  131.   };
  132.  
  133. struct {
  134.   BYTE  signal,pad;
  135.   ULONG signalflag;
  136.   WORD *buffer1;
  137.   WORD *buffer2;
  138.   ULONG bufferlen;
  139.   ULONG count;
  140.   ULONG offs;
  141.   APTR  task;
  142. } RecordData;
  143.  
  144. /*
  145.   The RecordFunc is written in assembler in order to
  146.   keep the CPU usage as low as possible. You are advised
  147.   to do the same.
  148.  
  149. __asm __saveds ULONG RecordFuncS(register __a0 struct Hook *hook,
  150.     register __a2 struct AHIAudioCtrl *actrl,
  151.     register __a1 struct AHIRecordMessage *chan)
  152. {
  153.   return NULL;
  154. }
  155. */
  156.  
  157. extern ULONG RecordFuncS();
  158. extern ULONG RecordFuncM();
  159.  
  160. struct Hook recordhook = 
  161. {
  162.   0,0,
  163.   NULL,
  164.   NULL,
  165.   &RecordData,
  166. };
  167.  
  168. BOOL openAHI(void);
  169. void closeAHI(void);
  170. void updatetopgadgets(void);
  171. long IDCMPhandler(void);
  172. void calcsamplesize(void);
  173. BOOL ProcessWindowWin0( LONG Class, UWORD Code, APTR IAddress );
  174. void record(void);
  175. void ulong2extended (ULONG in, extended *ex);
  176.  
  177. int main(void)
  178. {
  179.   struct StringNode *node;
  180.  
  181.   NewList(&InputList);
  182.   NewList(&OutputList);
  183.  
  184.   if (OpenLibs()==0)
  185.   {
  186.     if(openAHI())
  187.     {
  188.       OpenHardDiskRecordCatalog(NULL,NULL);
  189.       if (OpenWindowWin0("")==0)
  190.       {
  191.         if(filereq=AllocAslRequestTags(ASL_FileRequest,
  192.             ASLFR_Window,Win0,
  193.             ASLFR_SleepWindow,TRUE,
  194.             ASLFR_DoSaveMode,TRUE,
  195.             ASLFR_RejectIcons,TRUE,
  196.             TAG_DONE))
  197.         {
  198.           WindowLimits(Win0,200,Win0->BorderTop,0,0);
  199.           updatetopgadgets();
  200.           calcsamplesize();
  201.           IDCMPhandler();
  202.           FreeAslRequest(filereq);
  203.         }
  204.         CloseWindowWin0();
  205.       }
  206.       else
  207.         Printf("Cannot open window.\n");
  208.       CloseHardDiskRecordCatalog();
  209.       closeAHI();
  210.     }
  211.     else
  212.       Printf("Cannot open ahi.device\n");
  213.     CloseLibs();
  214.   }
  215.   else
  216.     Printf("Cannot open libraries.\n");
  217.  
  218.  
  219.   // These nodes are allocated in updatetopgadgets().
  220.   while(node=(struct StringNode *) RemHead(&InputList))
  221.     FreeVec(node);
  222.   while(node=(struct StringNode *) RemHead(&OutputList))
  223.     FreeVec(node);
  224. }
  225.  
  226. BOOL openAHI(void)
  227. {
  228.   if(AHImp=CreateMsgPort())
  229.   {
  230.     if(AHIio=(struct AHIRequest *)CreateIORequest(AHImp,sizeof(struct AHIRequest)))
  231.     {
  232.       AHIio->ahir_Version=2;
  233.       if((AHIDevice=OpenDevice(AHINAME,AHI_NO_UNIT,(struct IORequest *)AHIio,NULL)) == NULL)
  234.       {
  235.         AHIBase=(struct Library *)AHIio->ahir_Std.io_Device;
  236.         if(amreq=AHI_AllocAudioRequest(TAG_DONE))
  237.         {
  238.           return TRUE;
  239.         }
  240.       }
  241.     }
  242.   }
  243.   return FALSE;
  244. }
  245.  
  246. void closeAHI(void)
  247. {
  248.   if(amreq)
  249.     AHI_FreeAudioRequest(amreq);
  250.   if(!AHIDevice)
  251.     CloseDevice((struct IORequest *)AHIio);
  252.   DeleteIORequest((struct IORequest *)AHIio);
  253.   DeleteMsgPort(AHImp);
  254. }
  255.  
  256.  
  257. void updatetopgadgets(void)
  258. {
  259.   long i;
  260.   long inputs=0, outputs=0;
  261.   long minmon=0x00000, maxmon=0x00000, mingain=0x10000, maxgain=0x10000;
  262.   struct StringNode *node;
  263.  
  264.   amtext2[0]='\0';
  265.   sprintf(amtext1,GetString(SelectAudioID));
  266.  
  267.   AHI_GetAudioAttrs(audioid,NULL,
  268.       AHIDB_BufferLen,32,
  269.       AHIDB_Name,amtext1,
  270.       AHIDB_Inputs,&inputs,
  271.       AHIDB_Outputs,&outputs,
  272.       AHIDB_MinMonitorVolume,&minmon,
  273.       AHIDB_MaxMonitorVolume,&maxmon,
  274.       AHIDB_MinInputGain,&mingain,
  275.       AHIDB_MaxInputGain,&maxgain,
  276.       TAG_DONE);
  277.  
  278.   if(recfreq)
  279.     sprintf(amtext2,"%ld Hz",recfreq);
  280.  
  281.   // Create input and output lists
  282.  
  283.     // Free old lists first
  284.   while(node=(struct StringNode *) RemHead(&InputList))
  285.     FreeVec(node);
  286.   while(node=(struct StringNode *) RemHead(&OutputList))
  287.     FreeVec(node);
  288.  
  289.     // Add new nodes
  290.   for(i=0;i<inputs;i++)
  291.   {
  292.     if(node=AllocVec(sizeof(struct StringNode),MEMF_CLEAR))
  293.     {
  294.       AHI_GetAudioAttrs(audioid,NULL,
  295.         AHIDB_BufferLen,STRINGNODE_LENGTH,
  296.         AHIDB_InputArg,i,
  297.         AHIDB_Input,node->sn_String,
  298.         TAG_DONE);
  299.       node->sn_Node.ln_Name=node->sn_String;
  300.       node->sn_Node.ln_Type=STRINGNODE_ID;
  301.       AddTail(&InputList,(struct Node *) node);
  302.     }
  303.   }
  304.   for(i=0;i<outputs;i++)
  305.   {
  306.     if(node=AllocVec(sizeof(struct StringNode),MEMF_CLEAR))
  307.     {
  308.       AHI_GetAudioAttrs(audioid,NULL,
  309.         AHIDB_BufferLen,STRINGNODE_LENGTH,
  310.         AHIDB_OutputArg,i,
  311.         AHIDB_Output,node->sn_String,
  312.         TAG_DONE);
  313.       node->sn_Node.ln_Name=node->sn_String;
  314.       node->sn_Node.ln_Type=STRINGNODE_ID;
  315.       AddTail(&OutputList,(struct Node *) node);
  316.     }
  317.   }
  318.  
  319.   // Set gadget attributes
  320.  
  321.   GT_SetGadgetAttrs(Win0Gadgets[Win0_amtext1],Win0,NULL,
  322.       GTTX_Text,amtext1,
  323.       TAG_DONE);
  324.   GT_SetGadgetAttrs(Win0Gadgets[Win0_amtext2],Win0,NULL,
  325.       GTTX_Text,amtext2,
  326.       TAG_DONE);
  327.  
  328.   if(source >= inputs)
  329.     source=0;
  330.   GT_SetGadgetAttrs(Win0Gadgets[Win0_srclist],Win0,NULL,
  331.       GA_Disabled,!inputs,
  332.       GTLV_Labels,&InputList,
  333.       GTLV_Selected,source,
  334.       TAG_DONE);
  335.  
  336.   if(dest >= outputs)
  337.     dest=0;
  338.   GT_SetGadgetAttrs(Win0Gadgets[Win0_dstlist],Win0,NULL,
  339.       GA_Disabled,!outputs,
  340.       GTLV_Labels,&OutputList,
  341.       GTLV_Selected,dest,
  342.       TAG_DONE);
  343.  
  344.   if(vol<(minmon*100/0x10000))
  345.     vol=minmon*100/0x10000;
  346.   if(vol>(maxmon*100/0x10000))
  347.     vol=maxmon*100/0x10000;
  348.   GT_SetGadgetAttrs(Win0Gadgets[Win0_volslider],Win0,NULL,
  349.       GA_Disabled, minmon == maxmon,
  350.       GTSL_Min,minmon*100/0x10000,
  351.       GTSL_Max,maxmon*100/0x10000,
  352.       GTSL_Level,vol,
  353.       TAG_DONE);
  354.  
  355.   if(gain<(mingain*100/0x10000))
  356.     gain=mingain*100/0x10000;
  357.   if(gain>(maxgain*100/0x10000))
  358.     gain=maxgain*100/0x10000;
  359.   GT_SetGadgetAttrs(Win0Gadgets[Win0_gainslider],Win0,NULL,
  360.       GA_Disabled, mingain == maxgain,
  361.       GTSL_Min,mingain*100/0x10000,
  362.       GTSL_Max,maxgain*100/0x10000,
  363.       GTSL_Level,gain,
  364.       TAG_DONE);
  365. }
  366.  
  367. long IDCMPhandler(void)
  368. {
  369.   int done=0;
  370.   ULONG clas;
  371.   UWORD code;
  372.   struct Gadget *pgsel;
  373.   struct IntuiMessage *imsg;
  374.  
  375.   while(done==0)
  376.   {
  377.     Wait(1L << Win0->UserPort->mp_SigBit);
  378.     imsg=GT_GetIMsg(Win0->UserPort);
  379.     while (imsg != NULL )
  380.     {
  381.       clas=imsg->Class;
  382.       code=imsg->Code;
  383.       pgsel=(struct Gadget *)imsg->IAddress; /* Only reference if it is a gadget message */
  384.       GT_ReplyIMsg(imsg);
  385.       if(ProcessWindowWin0(clas, code, pgsel))
  386.         done=1;
  387.       imsg=GT_GetIMsg(Win0->UserPort);
  388.     }
  389.   }
  390.   return NULL;
  391. }
  392.  
  393. void calcsamplesize(void)
  394. {
  395.   samplesize = (type ? sizeof(AIFCheader) : sizeof(AIFFheader)) +\
  396.       (((struct StringInfo *) Win0Gadgets[Win0_duration]->SpecialInfo)->LongInt )*\
  397.       recfreq*(stereo ? 4 : 2);
  398.   GT_SetGadgetAttrs(Win0Gadgets[Win0_length],Win0,NULL,
  399.       GTNM_Number,samplesize/1024,
  400.       TAG_DONE);
  401. }
  402.  
  403. BOOL ProcessWindowWin0( LONG Class, UWORD Code, APTR IAddress )
  404. {
  405.   struct Gadget *gad;
  406.  
  407.   switch ( Class )
  408.   {
  409.   case IDCMP_GADGETUP :
  410.   case IDCMP_GADGETDOWN :
  411.     /* Gadget message, gadget = gad. */
  412.     gad = (struct Gadget *)IAddress;
  413.     switch ( gad->GadgetID ) 
  414.       {
  415.       case Win0_srclist :
  416.         /* ListView pressed, Text of gadget : Source */
  417.         source=Code;
  418.         break;
  419.       case Win0_dstlist :
  420.         /* ListView pressed, Text of gadget : Loopback dest. */
  421.         dest=Code;
  422.         break;
  423.       case Win0_volslider :
  424.         /* Slider changed  , Text of gadget : Loopback volume */
  425.         vol=Code;
  426.         break;
  427.       case Win0_ambutton :
  428.         /* Button pressed  , Text of gadget : Select audio mode... */
  429.         if(AHI_AudioRequest(amreq,
  430.             AHIR_Window,Win0,
  431.             AHIR_SleepWindow,TRUE,
  432.             AHIR_InitialAudioID,audioid,
  433.             AHIR_InitialMixFreq,recfreq,
  434.             AHIR_DoMixFreq,TRUE,
  435.             AHIR_FilterTags,&filtertags,
  436.             TAG_DONE))
  437.         {
  438.           audioid=amreq->ahiam_AudioID;
  439.           recfreq=amreq->ahiam_MixFreq;
  440.           AHI_GetAudioAttrs(audioid,NULL,
  441.               AHIDB_Stereo,&stereo,
  442.               TAG_DONE);
  443.           updatetopgadgets();
  444.           calcsamplesize();
  445.         }
  446.         break;
  447.       case Win0_gainslider :
  448.         /* Slider changed  , Text of gadget : Input gain */
  449.         gain=Code;
  450.         break;
  451.       case Win0_duration :
  452.         /* Integer entered , Text of gadget : Sample duration */
  453.         calcsamplesize();
  454.         break;
  455.       case Win0_filename :
  456.         /* String entered  , Text of gadget :  */
  457.         break;
  458.       case Win0_fnbutton :
  459.         /* Button pressed  , Text of gadget : File name... */
  460.  
  461.         // Copy the path to directory and the file name to filename
  462.         stccpy(directory,((struct StringInfo *) Win0Gadgets[Win0_filename]->SpecialInfo)->Buffer,1024);
  463.         ((char *) PathPart(directory))[0]='\0'; // Strip file name
  464.         stccpy(filename,FilePart(((struct StringInfo *) Win0Gadgets[Win0_filename]->SpecialInfo)->Buffer),1024);
  465.  
  466.         if(AslRequestTags(filereq,
  467.             ASLFR_InitialDrawer,directory,
  468.             ASLFR_InitialFile,filename,
  469.             TAG_DONE))
  470.         {
  471.           stccpy(fullname,filereq->fr_Drawer,1024);
  472.           AddPart(fullname,filereq->fr_File,1024);
  473.           GT_SetGadgetAttrs(Win0Gadgets[Win0_filename],Win0,NULL,
  474.               GTST_String,fullname,
  475.               TAG_DONE);
  476.         }
  477.         break;
  478.       case Win0_create :
  479.         /* Button pressed  , Text of gadget : Prepare sample file */
  480.         calcsamplesize();
  481.         if(file=Open(((struct StringInfo *) Win0Gadgets[Win0_filename]->SpecialInfo)->Buffer,
  482.             MODE_NEWFILE))
  483.         {
  484.           if(SetFileSize(file, samplesize, OFFSET_BEGINNING) == -1)
  485.             DisplayBeep(Win0->WScreen);
  486.           Close(file);
  487.           file=NULL;
  488.         }
  489.         break;
  490.       case Win0_begin :
  491.         /* Button pressed  , Text of gadget : Begin recording */
  492.         record();
  493.         break;
  494.       case Win0_format :
  495.         /* Cycle changed   , Text of gadget : File format */
  496.         type=Code;
  497.         calcsamplesize();
  498.         break;
  499.       }
  500.     break;
  501.   case IDCMP_CLOSEWINDOW :
  502.     return TRUE; // Return and signal quit
  503.   case IDCMP_REFRESHWINDOW :
  504.     GT_BeginRefresh( Win0);
  505.     /* Refresh window. */
  506.     RendWindowWin0( Win0, Win0VisualInfo );
  507.     GT_EndRefresh( Win0, TRUE);
  508.     GT_RefreshWindow( Win0, NULL);
  509.     RefreshGList( Win0GList, Win0, NULL, ~0);
  510.     break;
  511.   }
  512.   return FALSE;
  513. }
  514.  
  515. /*
  516. ** This is the function that does the actual recording.
  517. */
  518.  
  519. void record(void)
  520. {
  521.   WORD  error=-1,taskpri;
  522.   ULONG signalset,buffersize;
  523.   LONG  samples=(((struct StringInfo *) Win0Gadgets[Win0_duration]->SpecialInfo)->LongInt )*recfreq;
  524.  
  525.   taskpri=SetTaskPri(FindTask(NULL),1);
  526.   if(taskpri>1)
  527.     SetTaskPri(FindTask(NULL),taskpri);
  528.  
  529.   RecordData.offs=0;
  530.  
  531. // RecordData.task is the task that the RecordFunc will signal
  532.  
  533.   RecordData.task=FindTask(NULL);
  534.  
  535. // Open the (hopefully prepared)
  536.  
  537.   if(file=Open(((struct StringInfo *) Win0Gadgets[Win0_filename]->SpecialInfo)->Buffer,
  538.       MODE_OLDFILE))
  539.   {
  540.  
  541. // Test if file size is correct (i.e., is the file prepared?).
  542.  
  543.     Seek(file,0,OFFSET_END);
  544.     if(Seek(file,0,OFFSET_BEGINNING) == samplesize)
  545.     {
  546.  
  547. // Allocate the hardware
  548.  
  549.       if(actrl=AHI_AllocAudio(
  550.           AHIA_AudioID,audioid,
  551.           AHIA_MixFreq,recfreq,
  552.           AHIA_Channels,1,
  553.           AHIA_Sounds,1,
  554.           AHIA_RecordFunc,&recordhook,
  555.           TAG_DONE))
  556.       {
  557.  
  558. // Get the actual mixing/recording frequency
  559.  
  560.         AHI_ControlAudio(actrl,
  561.             AHIC_MixFreq_Query,&recfreq,
  562.             TAG_DONE);
  563.  
  564. // Allocate a signal for communication between this process and the RecordFunc.
  565.  
  566.         if(RecordData.signal=AllocSignal(-1) != -1)
  567.         {
  568.           RecordData.signalflag=(1L << RecordData.signal);
  569.  
  570. // Chose the correct RecordFunc
  571.  
  572.           if(stereo)
  573.             recordhook.h_Entry=&RecordFuncS;
  574.           else
  575.             recordhook.h_Entry=&RecordFuncM;
  576.  
  577. // Make sure our own buffers are larger (or equal) than AHI's.
  578.  
  579.           AHI_GetAudioAttrs(AHI_INVALID_ID,actrl,
  580.               AHIDB_MaxRecordSamples,&RecordData.bufferlen);
  581.           RecordData.bufferlen=max(RecordData.bufferlen,8192);
  582.  
  583. // Init RecordData.count
  584.  
  585.           RecordData.count=RecordData.bufferlen;
  586.  
  587. // buffersize is the size in bytes instead of sample frames.
  588.  
  589.           buffersize=RecordData.bufferlen*2*(stereo ? 2 : 1);
  590.  
  591. // Allocate own buffers (2 of them).
  592.  
  593.           if(RecordData.buffer1=AllocVec(buffersize,MEMF_PUBLIC))
  594.           {
  595.             if(RecordData.buffer2=AllocVec(buffersize,MEMF_PUBLIC))
  596.             {
  597.  
  598. // Write IFF headers.
  599.  
  600. // Why support both AIFF and AIFC? AIFF is obsolete, but still many Amiga programs
  601. // cannot handle AIFC files. That's why.
  602.  
  603.               if(type == 0)
  604.               {
  605.                 // Fill rest of the AIFFheader structure
  606.                 AIFFheader.FORMsize=samplesize-8;
  607.                 AIFFheader.COMMchunk.numChannels=(stereo ? 2 : 1);
  608.                 AIFFheader.COMMchunk.numSampleFrames=samples;
  609.                 ulong2extended(recfreq,&AIFFheader.COMMchunk.sampleRate);
  610.                 AIFFheader.SSNDsize=sizeof(SampledSoundHeader)+2*samples*AIFFheader.COMMchunk.numChannels;
  611.                 Write(file,&AIFFheader,sizeof(AIFFheader));
  612.               }
  613.               else if (type == 1)
  614.               {
  615.                 // Fill rest of the AIFCheader structure
  616.                 AIFCheader.FORMsize=samplesize-8;
  617.                 AIFCheader.COMMchunk.numChannels=(stereo ? 2 : 1);
  618.                 AIFCheader.COMMchunk.numSampleFrames=samples;
  619.                 ulong2extended(recfreq,&AIFCheader.COMMchunk.sampleRate);
  620.                 AIFCheader.SSNDsize=sizeof(SampledSoundHeader)+2*samples*AIFCheader.COMMchunk.numChannels;
  621.                 Write(file,&AIFCheader,sizeof(AIFCheader));
  622.               }
  623.  
  624. // Why do I have two calls here? It's just to make sure no samples are
  625. // collected until the hardware is set up correctly.
  626.  
  627.               AHI_ControlAudio(actrl,
  628.                   AHIC_MonitorVolume,vol*65536/100,
  629.                   AHIC_InputGain,gain*65536/100,
  630.                   AHIC_Input,source,
  631.                   AHIC_Output,dest,
  632.                   TAG_DONE);
  633.  
  634.               if(!AHI_ControlAudio(actrl,
  635.                   AHIC_Record,TRUE,
  636.                   TAG_DONE))
  637.               {
  638.  
  639.                 do
  640.                 {
  641.  
  642. // Update the 'seconds left' gadget
  643.                   GT_SetGadgetAttrs(Win0Gadgets[Win0_secleft],Win0,NULL,
  644.                       GTNM_Number,samples/recfreq,
  645.                       TAG_DONE);
  646.  
  647. // Wait until our RecordFunc has filled first buffer
  648. // RecordFunc always fills buffer1, which means that we should be saving buffer2.
  649.  
  650.                   signalset=Wait( RecordData.signalflag | SIGBREAKF_CTRL_C);
  651.  
  652. // Check if we've reached the end.
  653.  
  654.                   if( (samples -= RecordData.bufferlen)>0 )
  655.                   {
  656. // Write buffer 
  657.                     Write(file, RecordData.buffer2, buffersize);
  658.                   }
  659.                   else
  660.                   {
  661.  
  662. // Write the last part, update the 'seconds left' gadget and break from the do-while block
  663.  
  664.                     Write(file, RecordData.buffer2, 2*(samples+RecordData.bufferlen)*(stereo ? 2:1) );
  665.                     GT_SetGadgetAttrs(Win0Gadgets[Win0_secleft],Win0,NULL,
  666.                         GTNM_Number,0,
  667.                         TAG_DONE);
  668.                     break;
  669.                   }
  670.                 } while(!(signalset & SIGBREAKF_CTRL_C));
  671.                 error=0;
  672.               }
  673.  
  674. // Turn of RecordFunc
  675.  
  676.               AHI_ControlAudio(actrl,
  677.                   AHIC_Record,FALSE,
  678.                   TAG_DONE);
  679.  
  680. // Clean up...
  681.  
  682.               FreeVec(RecordData.buffer2);
  683.             }
  684.             FreeVec(RecordData.buffer1);
  685.           }
  686.           FreeSignal(RecordData.signal);
  687.         }
  688.         AHI_FreeAudio(actrl);
  689.       }
  690.       else
  691.         error=2;
  692.     }
  693.     else
  694.       error=1;
  695.     Close(file);
  696.   }
  697.   else
  698.     error=1;
  699.  
  700. // If an error occured, display a message.
  701.  
  702.   switch(error)
  703.   {
  704.     case 0:
  705.       req.es_TextFormat=GetString(Finished);
  706.       req.es_GadgetFormat=GetString(OKtext);
  707.       EasyRequestArgs(Win0, &req, NULL, NULL);
  708.       break;
  709.     case 1:
  710.       req.es_TextFormat=GetString(FileNotPrepared);
  711.       req.es_GadgetFormat=GetString(OKtext);
  712.       EasyRequestArgs(Win0, &req, NULL, NULL);
  713.       break;
  714.     case 2:
  715.       req.es_TextFormat=GetString(NoHardware);
  716.       req.es_GadgetFormat=GetString(OKtext);
  717.       EasyRequestArgs(Win0, &req, NULL, NULL);
  718.       break;
  719.     default:
  720.       req.es_TextFormat=GetString(UnknownErr);
  721.       req.es_GadgetFormat=GetString(OKtext);
  722.       EasyRequestArgs(Win0, &req, NULL, NULL);
  723.       break;
  724.   }
  725. // Restore the process priority.
  726.   SetTaskPri(FindTask(NULL),taskpri);
  727. }
  728.  
  729. // This function translates an ULONG to Apples SANE Extended used in AIFF/AIFC files.
  730.  
  731. void ulong2extended (ULONG in, extended *ex)
  732. {
  733.   ex->exponent=31+16383;
  734.   ex->mantissa[1]=0;
  735.   while(!(in & 0x80000000))
  736.   {
  737.     ex->exponent--;
  738.     in<<=1;
  739.   }
  740.   ex->mantissa[0]=in;
  741. }
  742.